package com.domi.route;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.yaml.snakeyaml.Yaml;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

@Slf4j
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository {

    private String dataId;

    private String group;

    private String serverAddr;

    private String namespace;

    private ApplicationEventPublisher applicationEventPublisher;

    private ConfigService configService;

    private Yaml yaml = new Yaml();

    public NacosRouteDefinitionRepository(ApplicationEventPublisher applicationEventPublisher,
                                          String serverAddr, String namespace, String dataId, String group) {
        this.applicationEventPublisher = applicationEventPublisher;
        this.serverAddr = serverAddr;
        this.namespace = namespace;
        this.dataId = dataId;
        this.group = group;
        this.configService = createConfigService();
        this.addListener();
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        try {
            log.info("=======================================================");
            log.info("getRouteDefinitions");
            log.info("=======================================================");
            String configContent = configService.getConfig(dataId, group, 5000);
            log.info("gateway routes: {}", configContent);
            List<RouteDefinition> routeDefinitions = yaml.load(configContent);
            return Flux.fromIterable(routeDefinitions);
        } catch (Exception e) {
            log.error("fetch config by nacos fail", e);
        }
        return null;
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        log.info("=======================================================");
        log.info("save");
        log.info("=======================================================");
        return null;
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        log.info("=======================================================");
        log.info("delete");
        log.info("=======================================================");
        return null;
    }

    private void addListener() {
        try {
            configService.addListener(dataId, group, new Listener() {
                @Override
                public Executor getExecutor() {
                    log.info("=======================================================");
                    log.info("getExecutor");
                    log.info("=======================================================");
                    return null;
                }

                @Override
                public void receiveConfigInfo(String configInfo) {
                    log.info("=======================================================");
                    log.info("receiveConfigInfo");
                    log.info("=======================================================");
                    applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
                }
            });
        } catch (NacosException e) {
            log.error("add nacos listener fail", e);
        }
    }

    private ConfigService createConfigService() {
        ConfigService configService = null;
        Properties properties = new Properties();
        properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddr);
        properties.setProperty(PropertyKeyConst.NAMESPACE, namespace);
        try {
            configService = NacosFactory.createConfigService(properties);
        } catch (NacosException e) {
            log.error("create ConfigService fail", e);
        }
        return configService;
    }

}